package com.github.limit.holder;

import com.github.limit.advise.LimitInterface;
import com.github.limit.annotation.Limit;
import com.github.limit.util.BeanUtils;
import com.github.limit.util.LimitUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Stream;


/**
 * @author: wangdejun
 * @create: 2019-10-14 09:27
 * <p>
 * LimitAnnotationHolder的默认实现，根据方法获取注解
 **/
@Slf4j
public class DefaultLimitAnnotationHolder extends AbstractLimitAnnotationHolder {


    @Override
    public void initMethodAnnotation(ApplicationContext applicationContext) throws Exception {

        // 初始注解和方法的映射关系
        // 1.类上标注了Limit注解
        Map<Method, List<Annotation>> classMethodListMap = initClassMethodAnnotation(applicationContext);

        // 方法上标注了注解的映射关系
        Map<Method, List<Annotation>> methodListMap = initMethodsAnnotation(applicationContext);


        // 构建所有包含注解的方法
        Set<Method> methods = new HashSet<>();
        methods.addAll(classMethodListMap.keySet());
        methods.addAll(methodListMap.keySet());

        doInitMethodAnnotation(methods, classMethodListMap, methodListMap);

        // 初始化AOP方法和注解映射
        initAopMethodAnnotation(applicationContext);

    }

    private void doInitMethodAnnotation(Set<Method> methods, Map<Method,
            List<Annotation>> classMethodListMap,
                                        Map<Method, List<Annotation>> methodListMap) {
        methods.stream().forEach(method -> {
            List<Annotation> classAnnotations = classMethodListMap.get(method);
            List<Annotation> methodAnnotations = methodListMap.get(method);
            List<Annotation> annotations = LimitUtils.mergeAndSort(methodAnnotations, classAnnotations);
            super.setMethodAnnotation(method, annotations);
        });


    }


    // 根据类上的注解构建方法和Annotation的映射关系
    private Map<Method, List<Annotation>> initClassMethodAnnotation(ApplicationContext applicationContext) {
        Map<Method, List<Annotation>> methodListMap = new HashMap<>();
        // 获取标注为Controller的类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Limit.class);
        beansWithAnnotation.values().stream().forEach(value -> {
            Class<?> clazz = value.getClass();
            List<Annotation> annotations = getClassAnnotation(clazz);
            Stream.of(clazz.getMethods()).filter(method -> method.getDeclaringClass() != Object.class).forEach(method -> methodListMap.putIfAbsent(method, annotations));
        });
        return methodListMap;

    }

    // 扫描所有controller，构建注方法和类的映射关系
    private Map<Method, List<Annotation>> initMethodsAnnotation(ApplicationContext applicationContext) {
        Map<Method, List<Annotation>> methodListMap = new HashMap<>();
        // 获取标注为Controller的类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Component.class);
        beansWithAnnotation.values().stream().forEach(value -> {
            Class<?> clazz = value.getClass();
            Stream.of(clazz.getMethods()).filter(method -> method.getDeclaringClass() != Object.class)
                    .forEach(method -> {
                        List<Annotation> methodAnnotation = getMethodAnnotation(method);
                        if (!CollectionUtils.isEmpty(methodAnnotation)) {
                            methodListMap.put(method, methodAnnotation);
                        }
                    });
        });

        return methodListMap;

    }

    // 获取方法山的注解
    private List<Annotation> getMethodAnnotation(Method method) {
        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
        Annotation[] annotations = method.getAnnotations();
        return LimitUtils.filterAnnotation(declaredAnnotations);
    }


    // 获取相应的注解
    private List<Annotation> getClassAnnotation(Class<?> clazz) {
        Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
        return LimitUtils.filterAnnotation(declaredAnnotations);
    }

    private void initAopMethodAnnotation(ApplicationContext applicationContext) {
        String[] beanNamesForType = applicationContext.getBeanNamesForType(LimitInterface.class);

        //Map<Method, List<Annotation>> methodListMap = new HashMap<>();

        Stream.of(beanNamesForType).forEach(beanName -> {
            Object bean = applicationContext.getBean(beanName);

            Class<?> clazz = BeanUtils.getTargetBean(bean);

            // 获取类上标注的注解
            List<Annotation> annotations = getClassAnnotation(clazz);

            Stream.of(clazz.getMethods()).filter(method -> method.getDeclaringClass() != Object.class)
                    .forEach(method -> {
                        List<Annotation> methodAnnotation = getMethodAnnotation(method);
                        // 合并类上和方法上的注解并排序，方法上的注解优先
                        List<Annotation> mergeAndSort = LimitUtils.mergeAndSort(methodAnnotation, annotations);

                        if (!CollectionUtils.isEmpty(mergeAndSort)) {
//                            methodListMap.put(method, mergeAndSort);
                            super.setMethodAnnotation(method, mergeAndSort);
                        }
                    });
        });

        /*for (Map.Entry<Method, List<Annotation>> entry : methodListMap.entrySet()) {
            super.setMethodAnnotation(entry.getKey(), entry.getValue());
        }*/


    }

}
